package sg.edu.ntu.metaweb.pastry;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;

import rice.environment.Environment;
import rice.p2p.commonapi.Id;
import rice.p2p.commonapi.Message;
import rice.p2p.past.Past;
import rice.p2p.past.PastImpl;
import rice.p2p.past.gc.GCPast;
import rice.p2p.past.gc.GCPastImpl;
import rice.pastry.NodeIdFactory;
import rice.pastry.PastryNode;
import rice.pastry.PastryNodeFactory;
import rice.pastry.commonapi.PastryIdFactory;
import rice.pastry.socket.SocketPastryNodeFactory;
import rice.pastry.standard.RandomNodeIdFactory;
import rice.persistence.LRUCache;
import rice.persistence.MemoryStorage;
import rice.persistence.PersistentStorage;
import rice.persistence.Storage;
import rice.persistence.StorageManagerImpl;
import sg.edu.ntu.metaweb.http.RequestHandler;
import sg.edu.ntu.metaweb.pastry.messages.RequestWebpageDataMessage;
import sg.edu.ntu.metaweb.pastry.messages.RequestRateWebpageMessage;
import sg.edu.ntu.metaweb.storage.StorageManager;
import sg.edu.ntu.metaweb.storage.data.Rating;

public class PastryNodeHandle {

	private Environment env = null;
	public PastryIdFactory pastryIdFactory = null;
	public PastryApplication pastryApplication = null;
	public MessageHandler messageHandler = null;
	public HTTPConnectionManager httpConnectionManager = null;
	public StorageManager storageManager;
	public PastryNode pastryNode = null;
	
	private int bindPort = -1;
	private String bootAddress = ""; 
	private int bootPort = -1;
	
	public PastryNodeHandle(int bindPort, String bootAddress, int bootPort) {
		env = new Environment();
		
		this.bindPort = bindPort;
		this.bootAddress = bootAddress;
		this.bootPort = bootPort;
	}
	
	
	public PastryNode getPastryNode() {
		return this.pastryNode;
	}
	
	public boolean initilize() {
	
		try {
			InetAddress bootInetAddress = InetAddress.getByName(bootAddress);
			InetSocketAddress bootInetSocketAddress = new InetSocketAddress(bootInetAddress, bootPort);
			
			
		    NodeIdFactory nidFactory = new RandomNodeIdFactory(env);
		    PastryNodeFactory factory = new SocketPastryNodeFactory(nidFactory, bindPort, env);
		    this.pastryNode = factory.newNode();
		    this.pastryApplication = new PastryApplication(pastryNode, this);
		    this.pastryIdFactory = new PastryIdFactory(env);
		    this.messageHandler = new MessageHandler(this);
		    this.httpConnectionManager = new HTTPConnectionManager();
		    this.storageManager = new StorageManager(this);
		    
		    this.pastryNode.boot(bootInetSocketAddress);
		    
		    // the node may require sending several messages to fully boot into the ring
		    synchronized(this.pastryNode) {
		    	while(!this.pastryNode.isReady() && !pastryNode.joinFailed()) {
		    		this.pastryNode.wait(500);
		    		if (this.pastryNode.joinFailed()) {
		    			throw new IOException("Could not join the FreePastry ring.  Reason:"+this.pastryNode.joinFailedReason()); 
		    		}
		     	}       
		    }
		    
		    
		    System.out.println("Finished creating new node " + this.pastryNode);   
		    return true;
		}
		catch (IOException exc) {
			System.out.println(exc.toString());
			return false;
		}
		catch (InterruptedException exc) {
			System.out.println(exc.toString());
			return false;
		}
		
	}
	
	
	public void handleRequest(RequestHandler requestHandler, HashMap<String, String> queryParamater) throws NumberFormatException {
		
		String doCommand = queryParamater.get("do_command"); 
		
		if (doCommand == null) {
			System.out.println("Missing do_command");
			return;
		}

		String urlString = queryParamater.get("url");
		URL url = null;
		try {
			url = new URL(urlString);
		}
		catch (MalformedURLException exc) {
			exc.printStackTrace();
			return;
		}
		
		Message msg;
		Id targetID = this.pastryIdFactory.buildId(urlString);
		
		if (doCommand.equalsIgnoreCase("update_page_rating")) {
			Rating rating = new Rating(Byte.parseByte(queryParamater.get("rating")));
			msg = new RequestRateWebpageMessage(this.pastryNode.getId(), targetID, requestHandler.getRequestID(), url, rating);
		}
		else if (doCommand.equalsIgnoreCase("get_page_data")) {
			msg = new RequestWebpageDataMessage(this.pastryNode.getId(), targetID, requestHandler.getRequestID(), url);
		}
		else {
			System.out.println("Unsupported do_command.");
			return;
		}
		
		this.sendMessage(targetID, msg);
	}
	
	public void sendMessage(Id id, Message msg) {
		this.pastryApplication.routeMyMsg(id, msg);
	}
	
	
	

}
